package com.zhengyao.dubbofilter;

import com.google.common.collect.Lists;
import com.zhengyao.config.MethodTakeTime;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @author : zhengyao3@郑瑶
 * @date : 2020/7/24 14:50
 * @Description: 方法级别监控耗时
 */
@Activate(group = CommonConstants.CONSUMER)
public class TPMonitorFilter implements Filter,Runnable {
    private final Map<String, List<MethodTakeTime>> methodMap = new ConcurrentHashMap<>();
    
    public TPMonitorFilter(){
        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(this, 10, 5, TimeUnit.SECONDS);
        //每60秒删除一次过期的数据
        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(()->{
            long now = System.currentTimeMillis() - 60 * 1000;
            methodMap.forEach((key,value)-> value.removeIf(item -> item.getEndTime() < now));
        }, 60, 60, TimeUnit.SECONDS);

    }
    
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        String methodName = invocation.getMethodName();
        long startTime = System.currentTimeMillis();
        Result invoke = invoker.invoke(invocation);
        long endTime = System.currentTimeMillis();
        //方法执行时间
        MethodTakeTime methodTakeTime = new MethodTakeTime();
        methodTakeTime.setStartTime(startTime);
        methodTakeTime.setEndTime(endTime);
        methodTakeTime.setTakeTime(endTime - startTime);
        //放入map中
        if (methodMap.containsKey(methodName)) {
            methodMap.get(methodName).add(methodTakeTime);
        } else {
            //线程安全的List
            List<MethodTakeTime> methodTakeTimeList =  Collections.synchronizedList(Lists.newArrayList());
            methodTakeTimeList.add(methodTakeTime);
            methodMap.put(methodName, methodTakeTimeList);
        }
        return invoke;
    }

    @Override
    public void run() {
        tpN(0.90f);
        tpN(0.99f);
    }

    /**
     * 计算TP数
     * @param n
     */
    private void tpN(float n){
        long now = System.currentTimeMillis() - 60 * 1000;
        methodMap.forEach((key,value)->{
            try{
            List<MethodTakeTime> methodTakeTimeLists = Lists.newArrayList();
            for (MethodTakeTime methodTakeTime : value) {
                if (methodTakeTime.getEndTime() > now) {
                    methodTakeTimeLists.add(methodTakeTime);
                }
            }
            //排序后按照TPN的数量取对应的耗时时间
            methodTakeTimeLists.sort((o1, o2) -> Math.toIntExact(o1.getTakeTime() - o2.getTakeTime()));
            int floor =(int) Math.floor(methodTakeTimeLists.size() * n);
            MethodTakeTime methodTakeTime = methodTakeTimeLists.get(floor);
            System.out.println("方法名："+key+" 的TP"+(n*100)+"值为："+methodTakeTime.getTakeTime());}
            catch (Exception e){
                System.out.println("计算TP数据时发生异常，异常原因为："+e.getMessage());
            }
        });
    }
}
